/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef js_Prefs_h
#define js_Prefs_h

#include "js/PrefsGenerated.h"

// [SMDOC] Prefs
//
// JS::Prefs is used to make JS preferences defined in StaticPrefList.yaml
// available to SpiderMonkey code.
//
// Adding a Pref
// =============
// Adding a new pref is easy. For example, if you're adding a new JS feature,
// you could add the following to StaticPrefList.yaml:
//
//   - name: javascript.options.experimental.my_new_feature
//     type: bool
//     value: false
//     mirror: always
//     set_spidermonkey_pref: startup
//
// The value of this pref can then be accessed in SpiderMonkey code with
// |JS::Prefs::experimental_my_new_feature()|.
//
// The default pref value in the YAML file applies to all SpiderMonkey builds
// (browser, JS shell, jsapi-tests, etc), so by default this feature will be
// disabled everywhere.
//
// To enable your feature, use the |--setpref experimental.my_new_feature=true|
// JS shell command line argument, or set the browser pref in about:config.
// Because this is a 'startup' pref, a browser restart is required for this to
// take effect.
//
// The rest of this comment describes more advanced use cases.
//
// Non-startup prefs
// =================
// Setting |set_spidermonkey_pref = startup| is recommended for most prefs.
// In this case the pref is only set during startup so we don't have to worry
// about the pref value changing at runtime.
//
// However, for some prefs this doesn't work. For instance, the WPT test harness
// can set test-specific prefs after startup. To properly update the JS pref in
// this case, |set_spidermonkey_pref = always| must be used. This means the
// SpiderMonkey pref will be updated whenever it's changed in the browser.
//
// Setting Prefs
// =============
// Embedders can override pref values. For startup prefs, this should only be
// done during startup (before calling JS_Init*) to avoid races with worker
// threads and to avoid confusing code with unexpected pref changes:
//
//   JS::Prefs::setAtStartup_experimental_my_new_feature(true);
//
// Non-startup prefs can also be changed after startup:
//
//   JS::Prefs::set_experimental_my_new_feature(true);
//
// JS Shell Prefs
// ==============
// The JS shell |--list-prefs| command line flag will print a list of all of the
// available JS prefs and their current values.
//
// To change a pref, use |--setpref name=value|, for example
// |--setpref experimental.my_new_feature=true|.
//
// It's also possible to add a custom shell flag. In this case you have to
// override the pref value yourself based on this flag.
//
// Fuzzing
// =======
// A pref that's not safe to toggle while fuzzing (for instance because the
// feature is experimental or incomplete) should be marked |fuzzing_safe: false|
// in StaticPrefList.yaml. When the JS shell runs with --fuzzing-safe, attempts
// to change such a pref are ignored with a warning.
//
// Testing Functions
// =================
// The |getAllPrefNames()| function will return an array with all JS pref names.
//
// The |getPrefValue(name)| function can be used to look up the value of the
// given pref. For example, use |getPrefValue("experimental.my_new_feature")|
// for the pref defined above.

namespace JS {

class Prefs {
  // For each pref, define a static |pref_| member.
  JS_PREF_CLASS_FIELDS;

  // When true, prefs that are marked |fuzzing_safe: false| can't be changed.
  // Set by the JS shell when the --fuzzing-safe flag is used.
  static bool fuzzingSafe_;
  static bool reportIgnoredFuzzingUnsafePrefs_;

  static void reportIgnoredFuzzingUnsafePref(const char* name);

#ifdef DEBUG
  static void assertCanSetStartupPref();
#else
  static void assertCanSetStartupPref() {}
#endif

 public:
  static void setFuzzingSafe(bool fuzzingSafe) { fuzzingSafe_ = fuzzingSafe; }
  static bool fuzzingSafe() { return fuzzingSafe_; }

  static void setReportIgnoredFuzzingUnsafePrefs(bool report) {
    reportIgnoredFuzzingUnsafePrefs_ = report;
  }

  // For each pref, define static getter/setter accessors.
#define DEF_GETSET(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF, \
                   FUZZING_SAFE)                                  \
  static TYPE CPP_NAME() { return CPP_NAME##_; }                  \
  static void SETTER(TYPE value) {                                \
    if (IS_STARTUP_PREF) {                                        \
      assertCanSetStartupPref();                                  \
    }                                                             \
    if (fuzzingSafe_ && !FUZZING_SAFE) {                          \
      reportIgnoredFuzzingUnsafePref(NAME);                       \
      return;                                                     \
    }                                                             \
    CPP_NAME##_ = value;                                          \
  }
  FOR_EACH_JS_PREF(DEF_GETSET)
#undef DEF_GETSET
};

};  // namespace JS

#endif /* js_Prefs_h */
